from buildutils import *

Import('env','build','install')
localenv = env.Clone()
localenv.Prepend(CPPPATH=['#include', '#src', 'shared'])
localenv.Append(CCFLAGS=env['warning_flags'])

# Turn off optimization to speed up compilation
ccflags = localenv['CCFLAGS']
for optimize_flag in ('-O3', '-O2', '/O2'):
    if optimize_flag in ccflags:
        ccflags.remove(optimize_flag)
localenv['CCFLAGS'] = ccflags

# Where possible, link tests against the shared libraries to minimize the sizes
# of the resulting binaries.
if localenv['OS'] == 'Linux':
    cantera_libs = localenv['cantera_shared_libs']
else:
    cantera_libs = localenv['cantera_libs']

localenv['ENV']['CANTERA_DATA'] = (Dir('#build/data').abspath + os.pathsep +
                                   Dir('#test/data').abspath)

# Add build/lib in order to find Cantera shared library
if env['OS'] == 'Windows' or env['OS'] == 'Cygwin':
    localenv.PrependENVPath('PATH', Dir('#build/lib').abspath)
elif env['OS'] == 'Darwin':
    localenv.PrependENVPath('DYLD_LIBRARY_PATH', Dir('#build/lib').abspath)
else:
    localenv.PrependENVPath('LD_LIBRARY_PATH', Dir('#build/lib').abspath)


PASSED_FILES = {}


class Test(object):
    def __init__(self, testName, subdir=None, programName=None,
                 blessedName='output_blessed.txt', arguments=(), options='',
                 artifacts=(), comparisons=(), tolerance=1e-5, threshold=1e-14,
                 dependencies=(), source_files=()):
        self.subdir = subdir or testName
        self.programName = programName or testName
        if isinstance(arguments, str):
            arguments = [arguments]
        self.blessedName = blessedName
        self.artifacts = artifacts
        if isinstance(self.artifacts, str):
            self.artifacts = [self.artifacts]
        self.comparisons = comparisons
        self.tolerance = tolerance  # error tolerance for CSV comparison
        self.threshold = threshold  # error threshold for CSV comparison
        self.source_files = source_files

        self.testName = testName
        self.passedFile = '.passed-%s' % testName
        PASSED_FILES[self.testName] = pjoin(self.subdir, self.passedFile)

        testResults.tests[self.testName] = self

        if source_files:
            self.program = localenv.Program(
                pjoin(self.subdir, self.programName), source_files,
                LIBS=self.libs or cantera_libs)
        else:
            if isinstance(self.programName, str):
                self.programName += '$PROGSUFFIX'
            self.program = [self.programName]
        command = self.program + [pjoin(self.subdir, arg) for arg in arguments]

        run = localenv.RegressionTest(pjoin(self.subdir, self.passedFile),
            command, active_test_name=testName,
            test_blessed_file=blessedName, test_command_options=options,
            test_comparisons=comparisons, test_csv_threshold=threshold,
            test_csv_tolerance=tolerance, test_ignoreLines=())

        localenv.Depends(env['test_results'], run)
        localenv.Depends(run, env['build_targets'])
        localenv.Alias('test-clean', self.clean(localenv))
        localenv.Alias('test-%s' % self.testName, run)
        env['testNames'].append(self.testName)

        # reset: just delete the ".passed" file so that this test will be re-run
        localenv.Alias('test-reset', self.reset(localenv))

        for dep in dependencies:
            localenv.Depends(run, dep)
        env.Depends(run, localenv.get('cantera_shlib', ()))

    def reset(self, env):
        f = pjoin(os.getcwd(), self.subdir, self.passedFile)
        if os.path.exists(f):
            uniqueName = 'reset-%s' % self.testName
            target = env.Command(uniqueName, [], [Delete(f)])
            return target

    def clean(self, env, files=None):
        # Name used for the output file
        if self.blessedName is not None and 'blessed' in self.blessedName:
            outName = self.blessedName.replace('blessed', 'output')
        else:
            outName = 'test_output.txt'

        files = files or []
        files += [self.passedFile, outName]
        files += list(self.artifacts)
        files += [comp[1] for comp in self.comparisons]
        files = [pjoin(os.getcwd(), self.subdir, name) for name in files]

        uniqueName = 'clean-%s-' % self.testName
        target = env.Command(uniqueName, [],
                             [Delete(f) for f in files
                              if os.path.exists(f)])
        return target

class CompileAndTest(Test):
    def __init__(self, testName, subdir=None, programName=None,
                 blessedName='output_blessed.txt', libs=(), extensions=('cpp',),
                 **kwargs):
        self.libs = list(libs)
        sources = mglob(env, subdir or testName, *extensions)
        Test.__init__(self, testName, subdir, programName, blessedName,
                      source_files=sources, **kwargs)

    def clean(self, env):
        basenames = [os.path.splitext(f.name)[0] for f in self.source_files]
        basenames.append(self.programName)
        exts = ['', '.o', '.exe', '.exe.manifest', '.ilk', '.obj', '.pdb', '.obj.pdb']
        files = [name + ext for name in set(basenames) for ext in exts]
        return Test.clean(self, env, files=files)


dhGraph = CompileAndTest('DH_graph_dilute', 'cathermo/DH_graph_1', 'DH_graph_1',
               'DH_NaCl_dilute_blessed.csv',
               artifacts=['DH_graph_1.log'],
               arguments='DH_NaCl_dilute.xml')
Test('DH_graph_acommon', 'cathermo/DH_graph_1',
     dhGraph.program, 'DH_NaCl_acommon_blessed.csv',
     artifacts=['DH_graph_1.log'],
     arguments='DH_NaCl_acommon.xml')
Test('DH_graph_bdotak', 'cathermo/DH_graph_1',
     dhGraph.program, 'DH_NaCl_bdotak_blessed.csv',
     artifacts=['DH_graph_1.log'],
     arguments='DH_NaCl_bdotak.xml')
Test('DH_graph_NM', 'cathermo/DH_graph_1',
     dhGraph.program, 'DH_NaCl_NM_blessed.csv',
     artifacts=['DH_graph_1.log'],
     arguments='DH_NaCl_NM.xml')
Test('DH_graph_Pitzer', 'cathermo/DH_graph_1',
     dhGraph.program, 'DH_NaCl_Pitzer_blessed.csv',
     artifacts=['DH_graph_1.log'],
     arguments='DH_NaCl_Pitzer.xml')

CompileAndTest('HMW_graph_CpvT', 'cathermo/HMW_graph_CpvT',
               extensions=['^HMW_graph_CpvT.cpp'],
               arguments=File('#test/data/HMW_NaCl_sp1977_alt.xml').abspath)
CompileAndTest('HMW_graph_GvI', 'cathermo/HMW_graph_GvI',
               blessedName=None,
               comparisons=[('T298_blessed.csv', 'T298.csv'),
                            ('T523_blessed.csv', 'T523.csv')],
               artifacts=['T373.csv','T423.csv','T473.csv',
                          'T548.csv','T573.csv'])
CompileAndTest('HMW_graph_GvT', 'cathermo/HMW_graph_GvT',
               extensions=['^HMW_graph_GvT.cpp'],
               arguments=File('#test/data/HMW_NaCl_sp1977_alt.xml').abspath)
CompileAndTest('HMW_graph_HvT', 'cathermo/HMW_graph_HvT',
               extensions=['^HMW_graph_HvT.cpp'],
               arguments=File('#test/data/HMW_NaCl_sp1977_alt.xml').abspath)
CompileAndTest('HMW_graph_VvT', 'cathermo/HMW_graph_VvT',
               extensions=['^HMW_graph_VvT.cpp'],
               arguments=File('#test/data/HMW_NaCl_sp1977_alt.xml').abspath)
CompileAndTest('HMW_test_1', 'cathermo/HMW_test_1',
               blessedName='output_noD_blessed.txt')
CompileAndTest('HMW_test_3', 'cathermo/HMW_test_3',
               blessedName='output_noD_blessed.txt')
CompileAndTest('IMSTester', 'cathermo/ims')
CompileAndTest('ISSPTester', 'cathermo/issp')
CompileAndTest('stoichSub', 'cathermo/stoichSub')
CompileAndTest('WaterPDSS', 'cathermo/testWaterPDSS')
CompileAndTest('WaterSSTP', 'cathermo/testWaterTP')
CompileAndTest('ISSPTester2', 'cathermo/VPissp')
CompileAndTest('ChemEquil_ionizedGas',
               comparisons=[('table_blessed.csv', 'table.csv')])
#CompileAndTest('CpJump', 'CpJump', 'CpJump', 'output_blessed.txt')
CompileAndTest('cxx_ex',
               comparisons=[('eq1_blessed.csv', 'eq1.csv'),
                            ('kin1_blessed.csv', 'kin1.csv'),
                            ('tr1_blessed.csv', 'tr1.csv'),
                            ('tr2_blessed.csv', 'tr2.csv')],
               tolerance=3e-3,
               threshold=1e-7,
               artifacts=['eq1.dat', 'kin1.dat', 'kin2.dat', 'kin3.csv',
                          'kin3.dat', 'tr1.dat', 'tr2.dat'])

diamond = CompileAndTest('diamondSurf-xml', 'diamondSurf', 'diamondSurf',
    'runDiamond_blessed.out', options='diamond_blessed.xml')
Test('diamondSurf-cti', 'diamondSurf', diamond.program, 'runDiamond_blessed.out',
     options='diamond.cti')
CompileAndTest('dustyGasTransport')
CompileAndTest('mixGasTransport')
CompileAndTest('multiGasTransport')

CompileAndTest('pureFluid', 'pureFluidTest')
CompileAndTest('rankine_democxx')
CompileAndTest('silane_equil')
CompileAndTest('stoichSolidKinetics')
CompileAndTest('surfkin')
CompileAndTest('surfSolver', 'surfSolverTest', 'surfaceSolver', None,
               arguments='haca2.cti',
               comparisons=[('results_blessed.txt', 'results.txt')],
               artifacts=['results.txt'],
               extensions=['^surfaceSolver.cpp'])
CompileAndTest('surfSolver2', 'surfSolverTest', 'surfaceSolver2', None,
               arguments='haca2.cti',
               comparisons=[('results2_blessed.txt', 'results2.txt')],
               artifacts=['results2.txt'],
               extensions=['^surfaceSolver2.cpp'])
CompileAndTest('VCS-NaCl', 'VCSnonideal/NaCl_equil',
               'nacl_equil', 'good_out.txt',
               options='-d 3',
               artifacts=['vcs_equilibrate_res.csv']), # not testing this file because it's not really csv

vcs_LiSi = CompileAndTest('VCS-LiSi', 'VCSnonideal/LatticeSolid_LiSi', 'latsol',
                          artifacts=['vcs_equilibrate_res.csv'])
Test('VCS-LiSi-verbose', 'VCSnonideal/LatticeSolid_LiSi', vcs_LiSi.program,
     'verbose_blessed.txt', options='8',
     artifacts=['vcs_equilibrate_res.csv'])
CompileAndTest('VPsilane_test')

CompileAndTest('clib', 'clib_test', 'clib_test',
               extensions=['^clib_test.c'], libs=['cantera_shared'])

if env['f90_interface'] == 'y':
    Test('f90-demo', 'fortran', '#build/samples/f90/demo', 'f90_demo_blessed.txt',
         threshold=1e-10)
    Test('f77-isentropic', 'fortran', '#build/samples/f77/isentropic',
         'f77_isentropic_blessed.txt')
    Test('f77-ctlib', 'fortran', '#build/samples/f77/ctlib', 'f77_ctlib_blessed.txt')

# Force explicitly-named tests to run even if SCons thinks they're up to date
for command in COMMAND_LINE_TARGETS:
    if command.startswith('test-'):
        name = command[5:]
        if name in PASSED_FILES and os.path.exists(PASSED_FILES[name]):
            os.remove(PASSED_FILES[name])
